#include "gunbuns.hpp"
#include "../score/score.hpp"

#include <iostream>

GunBuns::GunBuns() {
	phyWorld = NULL;
}

GunBuns::~GunBuns() {

}

void GunBuns::init(Engine *engine) {
	sky.SetImage(engine->getImage("data/gunbuns/sky.png"));
	background.SetImage(engine->getImage("data/gunbuns/background.png"));
	foreground.SetImage(engine->getImage("data/gunbuns/foreground.png"));

	for(std::map<int, Player>::iterator iPlayer = engine->players.begin(); iPlayer != engine->players.end(); iPlayer++) {
		sf::Sprite target;
		target.SetImage(engine->getImage("data/gunbuns/target.png"));
		target.SetColor((*iPlayer).second.color);
		target.SetCenter(sf::Vector2f(target.GetImage()->GetWidth() / 2.f, target.GetImage()->GetHeight() / 2.f));
		target.SetPosition(sf::Vector2f(sf::Randomizer::Random(0, 800), sf::Randomizer::Random(0, 600)));

		gunners[(*iPlayer).first].target = target;
		gunners[(*iPlayer).first].ammunition = 10;
		gunners[(*iPlayer).first].shot = false;
	}

	bunny.SetImage(engine->getImage("data/gunbuns/bunny.png"));
	bunny.SetCenter(sf::Vector2f(bunny.GetImage()->GetWidth() / 2.f, bunny.GetImage()->GetHeight()));
	newBunny();
	bunnyShotTime = 0.f;

	shot.SetBuffer(engine->getSound("data/gunbuns/shot.wav"));
	hit.SetBuffer(engine->getSound("data/gunbuns/hit.wav"));

	quit = false;
	quitTime = 2.f;

	countdown = 60.f;
	countdownString.SetFont(engine->getFont("data/fonts/BELIEVER.TTF"));
	countdownString.SetSize(32.f);
	countdownString.SetText("60");
	countdownString.SetPosition(400.f - countdownString.GetRect().GetWidth() / 2.f, 20.f);

	clock.Reset();
}

void GunBuns::cleanup() {
	gunners.clear();
	impacts.clear();
	scoreStrings.clear();
}

void GunBuns::pause() {

}

void GunBuns::resume() {
	clock.Reset();
}

void GunBuns::processEvent(Engine *engine, PlayerEvent& event) {
	switch(event.type) {
		case PlayerEvent::Quit:
		{
			if(event.state)
				engine->changeState(Score::getSingleton());
			break;
		}
		case PlayerEvent::Action1:
		{
			if(event.state)
				if(!quit)
					gunners[event.player].shot = true;
			break;
		}
		default:
			break;
	}
}

void GunBuns::update(Engine *engine) {

	float time = clock.GetElapsedTime();
	clock.Reset();

	for(std::list<Impact>::iterator iImpact = impacts.begin(); iImpact != impacts.end(); iImpact++) {
		(*iImpact).time -= time;
		(*iImpact).sprite.SetColor(sf::Color(255, 255, 255, int((*iImpact).time * 255)));
		if((*iImpact).time < 0.f) {
			iImpact = impacts.erase(iImpact);
			if(iImpact == impacts.end()) break;
		}
	}

	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++) {
		(*iScoreString).time -= time;
		if((*iScoreString).time <= 0.f) {
			iScoreString = scoreStrings.erase(iScoreString);
			if(iScoreString == scoreStrings.end()) break;
			continue;
		}

		(*iScoreString).string.Move(sf::Vector2f(0.f, -time * 32.f));
		(*iScoreString).string.SetColor(sf::Color(255, 255, 255, int(((*iScoreString).time) * 255)));
	}

	if(bunnyShotTime > 0.f) {
		bunnyShotTime -= time;
		if(bunnyShotTime <= 0.f) {
			newBunny();
			bunnyTime -= bunnyShotTime;
		}
	} else bunnyTime -= time;

	if((bunnyShotTime <= 0.f) && (bunnyTime <= 0.f)) {
		if(bunnyTime <= -3.f) newBunny();

		bunny.SetColor(sf::Color(0, 0, 0, int(bunnyTime / -3.f * 255)));

		if(bunnyDirection == 0){
		    if(bunnySense == 0 ) bunny.Move(sf::Vector2f(40.f * time, 0.f));
		    else bunny.Move(sf::Vector2f(-40.f * time, 0.f));
		} else bunny.Move(sf::Vector2f(0.f, -20.f * time));
	}

	if(countdown > 0.f) {
		countdown -= time;
		countdownString.SetText(val2str(int(countdown)));
		countdownString.SetPosition(400.f - countdownString.GetRect().GetWidth() / 2.f, 20.f);

		if(countdown <= 0.f) quit = true;
	}

	if(quit) {
		quitTime -= time;
		if(quitTime <= 0.f) engine->changeState(Score::getSingleton());
	}

	for(std::map<int, Gunner>::iterator iGunner = gunners.begin(); iGunner != gunners.end(); iGunner++) {
		int index = (*iGunner).first;
		Gunner& gunner = (*iGunner).second;

		if(gunner.target.GetScale().x > 1.0f) {
			float scale = gunner.target.GetScale().x - time;
			if(scale < 1.0f) scale = 1.0f;
			gunner.target.SetScale(scale, scale);
		}

		if((engine->players[index].actions[PlayerEvent::MoveLeft].state) && (!engine->players[index].actions[PlayerEvent::MoveRight].state))
			gunner.speed += sf::Vector2f(-1000.f * time, 0.f);
		else if((engine->players[index].actions[PlayerEvent::MoveRight].state) && (!engine->players[index].actions[PlayerEvent::MoveLeft].state))
			gunner.speed += sf::Vector2f(1000.f * time, 0.f);

		if((engine->players[index].actions[PlayerEvent::MoveUp].state) && (!engine->players[index].actions[PlayerEvent::MoveDown].state))
			gunner.speed += sf::Vector2f(0.f, -1000.f * time);
		else if((engine->players[index].actions[PlayerEvent::MoveDown].state) && (!engine->players[index].actions[PlayerEvent::MoveUp].state))
			gunner.speed += sf::Vector2f(0.f, 1000.f * time);

		if(gunner.speed.x < -500.f) gunner.speed.x = -500.f;
		else if(gunner.speed.x > 500.f) gunner.speed.x = 500.f;

		if(gunner.speed.y < -500.f) gunner.speed.y = -500.f;
		else if(gunner.speed.y > 500.f) gunner.speed.y = 500.f;

		gunner.speed *= (1.f - 1.f * time); // TODO: correct this, might blow up in our faces at any time

		gunner.target.Move(gunner.speed * time);
		if(gunner.target.GetPosition().x < 0.f) gunner.target.SetX(0.f);
		if(gunner.target.GetPosition().y < 0.f) gunner.target.SetY(0.f);
		if(gunner.target.GetPosition().x > 799.f) gunner.target.SetX(799.f);
		if(gunner.target.GetPosition().y > 599.f) gunner.target.SetY(599.f);

		if(gunner.shot) {
			if(gunner.ammunition > 0) {
				bool missed = true;
				shot.Play();
				gunner.ammunition--;
				if(gunner.ammunition == 0) gunner.ammunition = 10;
				gunner.target.SetScale(1.2f, 1.2f);

				/*if((bunnyShotTime <= 0.f) && (bunnyTime <= 0.f)) {
					if(foreground.GetPixel(int(gunner.target.GetPosition().x), int(gunner.target.GetPosition().y)).a == 0) {
						if((bunnyLayer == 1) || (background.GetPixel(int(gunner.target.GetPosition().x), int(gunner.target.GetPosition().y)).a == 0)) {
							int x = int(gunner.target.GetPosition().x - bunny.GetPosition().x + bunny.GetCenter().x),
								y = int(gunner.target.GetPosition().y - bunny.GetPosition().y + bunny.GetCenter().y);

							if((x >= 0) && (y >= 0) && (x < (int)bunny.GetImage()->GetWidth()) && (y < (int)bunny.GetImage()->GetHeight()))
								if(bunny.GetPixel(x, y).a > 0) {
									hit.Play();
									missed = false;
									if(bunnyLayer == 0) addScore(engine, index, 10);
									else addScore(engine, index, 5);
									bunnyShotTime = 1.f;
									bunny.SetColor(engine->players[index].color);
								}
						}
					}
				}*/

				if(missed) impact(engine, gunner.target.GetPosition());
			}
			gunner.shot = false;
		}
	}
}

void GunBuns::draw(Engine *engine) {
	engine->window.Draw(sky);
	engine->window.Draw(background);
	if(bunnyTime < 0.f) engine->window.Draw(bunny);
	engine->window.Draw(foreground);

	for(std::map<int, Gunner>::iterator iGunner = gunners.begin(); iGunner != gunners.end(); iGunner++)
		if((*iGunner).second.ammunition) engine->window.Draw((*iGunner).second.target);

	for(std::list<Impact>::iterator iImpact = impacts.begin(); iImpact != impacts.end(); iImpact++)
		engine->window.Draw((*iImpact).sprite);

	engine->window.Draw(countdownString);

	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++)
		engine->window.Draw((*iScoreString).string);
}

void GunBuns::impact(Engine *engine, sf::Vector2f position) {
	Impact impact;
	impact.sprite.SetImage(engine->getImage("data/gunbuns/impact.png"));
	impact.sprite.SetCenter(sf::Vector2f(impact.sprite.GetImage()->GetWidth() / 2.f, impact.sprite.GetImage()->GetHeight() / 2.f));
	impact.sprite.SetPosition(position);
	impact.time = 1.f;
	impacts.push_back(impact);
}

void GunBuns::newBunny() {
	bunnyDirection = sf::Randomizer::Random(0, 1);
	bunnySense = sf::Randomizer::Random(0, 1);
	int position = sf::Randomizer::Random(0, 2);

	if(bunnyDirection == 0) {
		if(bunnySense == 0) bunny.SetPosition(sf::Vector2f(668.f - bunny.GetImage()->GetWidth() / 2.f, 250.f));
		else if(bunnySense == 1) bunny.SetPosition(sf::Vector2f(700.f - bunny.GetImage()->GetWidth() / 2.f, 258.f));
	} else {
		if(position == 0) bunny.SetPosition(sf::Vector2f(70.f + bunny.GetImage()->GetWidth() / 2.f, 518.f));
		else if(position == 1) bunny.SetPosition(sf::Vector2f(375.f + bunny.GetImage()->GetWidth() / 2.f, 440.f));
		else bunny.SetPosition(sf::Vector2f(550.f + bunny.GetImage()->GetWidth() / 2.f, 573.f));
	}
	bunnyTime = sf::Randomizer::Random(1.f, 3.f);
}


void GunBuns::addScore(Engine *engine, int player, int score) {
	engine->players[player].score += score;
	if(engine->players[player].score < 0) engine->players[player].score = 0;

	sf::String string;
	string.SetText(val2str(score));
	if(score > 0) string.SetText("+" + string.GetText());
	string.SetFont(engine->getFont("data/fonts/BELIEVER.TTF"));
	string.SetSize(24.f);
	string.SetColor(sf::Color(255, 255, 255, 255));
	string.SetPosition(gunners[player].target.GetPosition());

	if(string.GetPosition().x + string.GetRect().GetWidth() > 800.f) string.Move(sf::Vector2f(800.f - (string.GetPosition().x + string.GetRect().GetWidth()), 0.f));
	if(string.GetPosition().y + string.GetRect().GetHeight() > 600.f) string.Move(sf::Vector2f(0.f, 600.f - (string.GetPosition().y + string.GetRect().GetHeight())));

	scoreStrings.push_back(ScoreString(string));
}
